﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dubbo.Rpc
{
    public abstract class AbstractInvoker<T> : IInvoker<T>
    {
        //protected readonly Logger logger = LoggerFactory.getLogger(getClass());
        
        private readonly Dictionary<string, string> attachment;

        private volatile bool available = true;

        private volatile bool destroyed = false;
        
        public bool IsDestroyed
        {
            get
            {
                return destroyed;
            }
            
        }


        public AbstractInvoker(URL url)
            : this(url, (Dictionary<string, string>)null)
        {

        }

        public AbstractInvoker(URL url, string[] keys)
            : this(url, ConvertAttachment(url, keys))
        {
        }

        public AbstractInvoker(URL url, Dictionary<string, string> attachment)
        {
            if (url == null)
                throw new ArgumentException("service url == null");
            this.Url = url;
            //this.attachment = attachment == null ? null : Collections.unmodifiableMap(attachment);
            this.attachment = attachment;
        }

        private static Dictionary<string, string> ConvertAttachment(URL url, string[] keys)
        {
            if (keys == null || keys.Length == 0)
            {
                return null;
            }
            Dictionary<string, string> attachment = new Dictionary<string, string>();
            foreach (string key in keys)
            {
                string value = url.GetParameter(key);
                if (!string.IsNullOrEmpty(value))
                {
                    attachment[key] = value;
                }
            }
            return attachment;
        }
        
        public URL Url { get; private set; }

        public virtual bool IsAvailable
        {
            get
            {
                return available;
            }
            protected set
            {
                this.available = value;
            }
        }

        public virtual void Destroy()
        {
            if (IsDestroyed)
            {
                return;
            }
            destroyed = true;
            IsAvailable = false;
        }

        public override string ToString()
        {
            return typeof(T) + " -> " + (Url == null ? "" : Url.ToString());
        }

        public IResult Invoke(IInvocation<T> inv)
        {
            if (destroyed)
            {
                //throw new RpcException("Rpc invoker for service " + this + " on consumer " + NetUtils.getLocalHost()
                //                                + " use dubbo version " + Version.getVersion()
                //                                + " is DESTROYED, can not be invoked any more!");
            }
            RpcInvocation<T> invocation = (RpcInvocation<T>)inv;
            invocation.Invoker = this;
            if (attachment != null && attachment.Count > 0)
            {
                invocation.AddAttachmentsIfAbsent(attachment);
            }
            //Dictionary<string, string> context = RpcContext.getContext().getAttachments();
            //if (context != null) {
            //    invocation.AddAttachmentsIfAbsent(context);
            //}
            //if (Url.getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
            //    invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.tostring());
            //}
            //RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);


            try
            {
                return DoInvoke(invocation);
            }
            //catch (InvocationTargetException e) { // biz exception
            //    Throwable te = e.getTargetException();
            //    if (te == null)
            //    {
            //        return new RpcResult(e);
            //    }
            //    else
            //    {
            //        if (te instanceof RpcException) {
            //            ((RpcException)te).setCode(RpcException.BIZ_EXCEPTION);
            //        }
            //        return new RpcResult(te);
            //    }
            //}
            //catch (RpcException e) {
            //    if (e.isBiz())
            //    {
            //        return new RpcResult(e);
            //    }
            //    else
            //    {
            //        throw e;
            //    }
            //}
            catch (Exception e)
            {
                return new RpcResult(e);
            }
        }

        protected abstract IResult DoInvoke(IInvocation<T> invocation);
    }
}
